package top.lyjwn.todo.common.utils;

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import top.lyjwn.todo.common.utils.DateYearsMonthsDays.Year;
import top.lyjwn.todo.common.utils.DateYearsMonthsDays.Year.Month;
import top.lyjwn.todo.common.utils.DateYearsMonthsDays.Year.Month.Day;

public class DateHandle {

    private Date date;
    private int year;// 年
    private int month;// 月
    private int day;// 日
    private int hour;// 小时
    private int minute;// 分钟
    private int second;// 秒
    private int week;// 星期几
    private int dayOfMonth;// 月中第几天
    private int dayOfYear;// 年中第几天

    // 本月最小日期参数
    public static final String MAX_DATE_OF_MONTH = "maxDateOfofMonth";
    // 本月最小日期参数
    public static final String MIN_DATE_OF_MONTH = "minDateOfofMonth";

    private Calendar calendar = Calendar.getInstance();

    public DateHandle(Date date) {
        this.date = date;
        this.calendar.setTime(date);
        this.year = calendar.get(Calendar.YEAR);
        this.month = calendar.get(Calendar.MONTH) + 1;
        this.day = calendar.get(Calendar.DATE);
        this.hour = calendar.get(Calendar.HOUR_OF_DAY);
        this.minute = calendar.get(Calendar.MINUTE);
        this.second = calendar.get(Calendar.SECOND);
        this.week = calendar.get(Calendar.DAY_OF_WEEK);
        this.dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        this.dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
    }

    public DateHandle setDate(Date date) {
        this.date = date;
        this.calendar.setTime(date);
        this.year = calendar.get(Calendar.YEAR);
        this.month = calendar.get(Calendar.MONTH) + 1;
        this.day = calendar.get(Calendar.DATE);
        this.hour = calendar.get(Calendar.HOUR_OF_DAY);
        this.minute = calendar.get(Calendar.MINUTE);
        this.second = calendar.get(Calendar.SECOND);
        this.week = calendar.get(Calendar.DAY_OF_WEEK);
        this.dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        this.dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
        return this;
    }

    public DateHandle() {
        this.date = new Date();
    }

    public static DateHandle instance() {
        return new DateHandle();
    }

    /**
     * 转换为新的Date
     *
     * @return
     */
    public Date toNewDate() {
        return calendar.getTime();
    }

    /**
     * 向前或者向后偏移的小时(hour)数
     *
     * @param hour 偏移的小时数。-负数向前;+正数向后
     * @return
     */
    public Date offsetHour(int hour) {
        this.setHour(hour + this.hour + hour);
        return this.toNewDate();
    }

    /**
     * @return 一个携带年月日的三级联列表, 存有从当前日期到输入date的所有年月日, 其中的数据为int类型
     */
    public List<Year> getYearMouthDay() {
        SimpleDateFormat YMDSdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat yearSdf = new SimpleDateFormat("yyyy");
        SimpleDateFormat monthSdf = new SimpleDateFormat("MM");
        SimpleDateFormat daySdf = new SimpleDateFormat("dd");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(this.date);
        // 将年月日转换成整型
        int beginYear = Integer.parseInt(yearSdf.format(calendar.getTime()));// 年
        int beginMonth = Integer.parseInt(monthSdf.format(calendar.getTime()));// 月
        int beginDay = Integer.parseInt(daySdf.format(calendar.getTime()));// 日
        int minBeginDay = calendar.getActualMinimum(Calendar.DATE);// 最小开始日
        calendar.clear();// 清空一次
        calendar.setTime(new Date());
        // 将年月日转换成整型
        int endYear = Integer.parseInt(yearSdf.format(calendar.getTime()));// 年
        int endMonth = Integer.parseInt(monthSdf.format(calendar.getTime()));// 月
        int endDay = Integer.parseInt(daySdf.format(calendar.getTime()));// 日
        int maxEndDay = calendar.getActualMaximum(Calendar.DATE);// 最大结束日
        List<Year> yearMouthDay = new ArrayList<>();
        for (int i = beginYear; i <= endYear; i++) {
            Year year = new Year();
            List<Month> monthList = new ArrayList<>();
            for (int j = (i == beginYear ? beginMonth : 1); j <= (i == endYear ? endMonth : 12); j++) {
                Month month = new Month();
                Calendar cj = Calendar.getInstance();
                cj.set(Calendar.YEAR, i);
                cj.set(Calendar.MONTH, j - 1);// 月份需要减一，这是Calendar的性质所导致，其实一个数组
                cj.set(Calendar.DAY_OF_MONTH, 1);
                month.setDate(cj.getTime());
                month.setDateString(YMDSdf.format(cj.getTime()));
                month.setMonth(j);
                List<Day> dayList = new ArrayList<>();
                for (int k = (i == beginYear && beginMonth == j ? beginDay : minBeginDay); k <= (i == endYear
                        && endMonth == j ? endDay : maxEndDay); k++) {
                    Day day = new Day();
                    Calendar ck = Calendar.getInstance();
                    ck.set(Calendar.YEAR, i);
                    ck.set(Calendar.MONTH, j - 1);// 月份需要减一，这是Calendar的性质所导致，其实一个数组
                    ck.set(Calendar.DAY_OF_MONTH, k);
                    day.setDate(ck.getTime());
                    day.setDateString(YMDSdf.format(ck.getTime()));
                    day.setDay(k);
                    dayList.add(day);
                }
                month.setDays(dayList);
                monthList.add(month);
            }
            Calendar ci = Calendar.getInstance();
            ci.set(Calendar.YEAR, i);
            ci.set(Calendar.MONTH, 1);
            ci.set(Calendar.DAY_OF_MONTH, 1);
            year.setDate(ci.getTime());
            year.setDateString(YMDSdf.format(ci.getTime()));
            year.setYear(i);
            year.setMonths(monthList);
            yearMouthDay.add(year);
        }
        return yearMouthDay;
    }

    /**
     * @param date
     * @return 传入的date当月的最小日期
     */
    public static Date getMinDateOfMouth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, 1 - calendar.get(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();

    }

    /**
     * @param date
     * @return 传入的date当月的最大日期
     */
    public static Date getMaxDateOfMouth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE) - calendar.get(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();

    }

    /**
     * @return 返回本类属性date当月的最小日期
     */
    public Date getMinDateOfMouth() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(this.date);
        calendar.add(Calendar.DATE, 1 - calendar.get(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();

    }


    /**
     * @return 返回本类属性date当月的最大日期
     */
    public Date getMaxDateOfMouth() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(this.date);
        calendar.add(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE) - calendar.get(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();

    }


    /**
     * @return 返回本类属性date当月的最大时间
     */
    public Date getMaxTimeOfDay() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(this.date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();

    }


    /**
     * @return 返回本类属性date当日的最小时间
     */
    public Date getMinTimeOfDay() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(this.date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();

    }

    /**
     * @return 返回本类属性date，的字符串yyyy-MM-dd
     */
    public String toString() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(this.date);
    }

    public String toString(String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(this.date);
    }

    /**
     * @return 返回本类属性date当月的最大日期
     * @throws Exception
     */
    public String toMaxString(String method) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        switch (method) {
            case MAX_DATE_OF_MONTH:
                return sdf.format(this.getMaxDateOfMouth());
            case MIN_DATE_OF_MONTH:
                return sdf.format(this.getMinDateOfMouth());
            default:
                throw new Exception("未知的转换类型");
        }

    }

    /**
     * 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
     *
     * @param strDate
     * @return
     */
    public static Date strToDateLong(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    /**
     * 将短时间格式字符串转换为时间 yyyy-MM-dd
     *
     * @param strDate
     * @return
     */
    public static Date strToDate(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }


    /**
     * 获取当前日期是星期几<br>
     *
     * @param date
     * @return 当前日期是星期几 {"日", "一", "二", "三", "四", "五", "六"}
     */
    public static String getWeekStrOfDate(Date date) {
        String[] weekDays = {"日", "一", "二", "三", "四", "五", "六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;
        return weekDays[w];
    }

    /**
     * 获取当前日期是星期几<br>
     *
     * @param dayOfWeek
     * @return 当前日期是星期几 {"日", "一", "二", "三", "四", "五", "六"}
     */
    public static String getWeekStrOfDate(DayOfWeek dayOfWeek) {
        switch (dayOfWeek) {
            case MONDAY:
                return "一";
            case TUESDAY:
                return "二";
            case WEDNESDAY:
                return "三";
            case THURSDAY:
                return "四";
            case FRIDAY:
                return "五";
            case SATURDAY:
                return "六";
            case SUNDAY:
                return "日";
            default:
                return "?";
        }
    }


    /**
     * 获取当前日期是星期几<br>
     *
     * @param date
     * @return 当前日期是星期几(数字)
     */
    public static int getWeekIntOfDate(Date date) {
        int[] weekDays = {0, 1, 2, 3, 4, 5, 6};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;
        return weekDays[w];
    }


    /**
     * 获取当前日期是星期几<br>
     *
     * @param dayOfWeek
     * @return 当前日期是星期几(数字)
     */
    public static int getWeekIntOfDate(DayOfWeek dayOfWeek) {
        switch (dayOfWeek) {
            case MONDAY:
                return 1;
            case TUESDAY:
                return 2;
            case WEDNESDAY:
                return 3;
            case THURSDAY:
                return 4;
            case FRIDAY:
                return 5;
            case SATURDAY:
                return 6;
            case SUNDAY:
                return 7;
            default:
                return 0;
        }
    }


    /**
     * LocalDateTime 转 Date
     *
     * @param localDateTime
     * @return
     */
    public static Date toDateByLocalDateTime(LocalDateTime localDateTime) {
        Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        return date;
    }

    /**
     * Date 转 LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime toLocalDateTimeByDate(Date date) {
        LocalDateTime ldt = date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
        return ldt;
    }

    /**
     * 计算年龄
     *
     * @param birthDate
     * @return
     */
    public static int age(Date birthDate, Date reference) {
        // 当前日历
        Calendar nowCalendar = Calendar.getInstance();
        nowCalendar.setTime(reference);
        // 生日大于当前日期
        if (nowCalendar.before(birthDate)) {
            throw new IllegalArgumentException("The birth date is before current time, it's unbelievable");
        }
        // 当前年月日
        int yearNow = nowCalendar.get(Calendar.YEAR);
        int monthNow = nowCalendar.get(Calendar.MONTH);
        int dayNow = nowCalendar.get(Calendar.DAY_OF_MONTH);
        // 出生日期年月日
        Calendar birthCalendar = Calendar.getInstance();
        birthCalendar.setTime(birthDate);
        int yearBirth = birthCalendar.get(Calendar.YEAR);
        int monthBirth = birthCalendar.get(Calendar.MONTH);
        int dayBirth = birthCalendar.get(Calendar.DAY_OF_MONTH);
        // 粗计算年龄
        int age = yearNow - yearBirth;
        // 当前月份小于出生月份年龄减一
        if (monthNow < monthBirth) {
            age--;
        }
        // 当前月份等于出生月份，日小于生日年龄减一
        else if (monthNow == monthBirth && dayNow < dayBirth) {
            age--;
        }
        // 返回年龄值
        return age;
    }

    /**
     * 计算年龄
     *
     * @param birthDate
     * @return
     */
    public static int age(LocalDate birthDate) {
        Period between = Period.between(birthDate, LocalDate.now());
        return between.getYears();
    }


    /**
     * 根据出生日期-当前日期计算，成人写整数即可；
     * 儿科的，一个月内写天数，
     * 半岁以内写几个月几天，
     * 半岁以上写几个月，
     * 一岁以上写几岁几个月，
     * 12岁以上用成人的写法
     *
     * @param birthDate 生日
     * @return 年龄
     */
    public static String getPatientAge(Date birthDate, Date reference) {
        // 当前日历
        Calendar nowCalendar = Calendar.getInstance();
        nowCalendar.setTime(reference);
        // 生日大于当前日期
        if (nowCalendar.before(birthDate)) {
            throw new IllegalArgumentException("The birth date is before current time, it's unbelievable");
        }
        // 当前年月日
        int yearNow = nowCalendar.get(Calendar.YEAR);
        int monthNow = nowCalendar.get(Calendar.MONTH);
        int dayNow = nowCalendar.get(Calendar.DAY_OF_MONTH);
        // 出生日期年月日
        Calendar birthCalendar = Calendar.getInstance();
        birthCalendar.setTime(birthDate);
        int yearBirth = birthCalendar.get(Calendar.YEAR);
        int monthBirth = birthCalendar.get(Calendar.MONTH);
        int dayBirth = birthCalendar.get(Calendar.DAY_OF_MONTH);
        // 粗计算年龄
        int age = yearNow - yearBirth;
        // 当前月份小于出生月份年龄减一
        if (monthNow < monthBirth) {
            age--;
        }
        // 当前月份等于出生月份，日小于生日年龄减一
        else if (monthNow == monthBirth && dayNow < dayBirth) {
            age--;
        }
        // 粗计月
        int month;
        if (monthNow < monthBirth) {
            month = 12 + monthNow - monthBirth;
        } else {
            month = monthNow - monthBirth;
        }
        int day;
        if (dayNow < dayBirth) {
            day = 30 + dayNow - dayBirth;
            month = month - 1;
        } else {
            day = dayNow - dayBirth;
        }
        if (age >= 12) {
            return age + "岁";
        } else if (age >= 1) {
            return age + "岁" + (month == 0 ? "" : (month + "月"));
        } else if (month >= 6) {
            return month + "月";
        } else if (month >= 1) {
            return month + "月" + (day == 0 ? "" : (day + "天"));
        }
        return day + "天";
    }


    public static String getPatientAge(LocalDate birthDate, LocalDate reference) {
        Period between = Period.between(birthDate, reference);
        int age = between.getYears();
        int month = between.getMonths();
        int day = between.getDays();
        if (between.getYears() >= 12) {
            return age + "岁";
        } else if (age >= 1) {
            return age + "岁" + (month == 0 ? "" : (month + "月"));
        } else if (month >= 6) {
            return month + "月";
        } else if (month >= 1) {
            return month + "月" + (day == 0 ? "" : (day + "天"));
        }
        return day + "天";
    }


    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
        calendar.set(Calendar.YEAR, year);
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
        calendar.set(Calendar.MONTH, month);
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
        calendar.set(Calendar.DATE, day);
    }

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        this.hour = hour;
        calendar.set(Calendar.HOUR_OF_DAY, hour);
    }

    public int getMinute() {
        return minute;
    }

    public void setMinute(int minute) {
        this.minute = minute;
        calendar.set(Calendar.MINUTE, minute);
    }

    public int getSecond() {
        return second;
    }

    public void setSecond(int second) {
        this.second = second;
        calendar.set(Calendar.SECOND, second);
    }

    public int getWeek() {
        return week;
    }

    public void setWeek(int week) {
        this.week = week;
        calendar.set(Calendar.DAY_OF_WEEK, week);
    }

    public int getDayOfMonth() {
        return dayOfMonth;
    }

    public void setDayOfMonth(int dayOfMonth) {
        this.dayOfMonth = dayOfMonth;
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
    }

    public int getDayOfYear() {
        return dayOfYear;
    }

    public void setDayOfYear(int dayOfYear) {
        this.dayOfYear = dayOfYear;
        calendar.set(Calendar.DAY_OF_YEAR, dayOfYear);
    }


    private static String[] dateTimeformatters = {
            "yyyy-MM-dd",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd'T'HH:mm:ss",
            "yyyy/MM/dd HH:mm:ss",
            "yyyyMMddHHmmss",
    };

    private static String[] dateformatters = {
            "yyyy-MM-dd",
            "yyyy/MM/dd",
            "yyyyMMdd",
    };

    /**
     * 字符串自动转换为localDateTime
     *
     * @param string
     * @return
     */
    public static LocalDateTime toLocalDateTime(String string) {
        for (String formatter : dateformatters) {
            try {
                return LocalDateTime.of(LocalDate.parse(string, DateTimeFormatter.ofPattern(formatter)), LocalTime.MIN);
            } catch (DateTimeParseException e) {
                // 继续尝试下一个格式
            }
        }
        for (String formatter : dateTimeformatters) {
            try {
                return toLocalDateTime(string, formatter);
            } catch (DateTimeParseException e) {
                // 继续尝试下一个格式
            }
        }
        throw new DateTimeParseException("日期转化失败", string, 0);
    }

    public static LocalDateTime toLocalDateTime(String string, String formatter) {
        return LocalDateTime.parse(string, DateTimeFormatter.ofPattern(formatter));
    }
}
